/* Copyright (c) 2008, Nozomi SATO. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of Nozomi Sato nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include "doBilinearLine.h"

static void getWVectorsAndCordinates(s32 *pWVectors, s16 *pCordinates, s32 srcWidth, s32 dstWidth)
{
	s32 x;
	s32 *p = pWVectors;
	s32 factor = (srcWidth << PRECISION_SHIFT) / dstWidth; // Q8 fixed point
	for (x = 0; x < dstWidth; x++) {
		s32 tmp1 = factor * x;
		s32 tmp = tmp1 >> PRECISION_SHIFT; // floor

		p[0] = tmp;
		if ((tmp + 1) >= srcWidth) {
			p[1] = tmp;
		} else {
			p[1] = tmp + 1;
		}

		s32 u_ratio = tmp1 - (tmp << PRECISION_SHIFT);

		pCordinates[x] = u_ratio;
		//fprintf(stdout, "u_ratio:%d\n", u_ratio);
		p += 2;
	}
}

static void getHRowPointersAndCordinates(u8 **pRowPointers, s16 *pCordinates, const u8 *pSrc, u32 srcPitch, s32 srcHeight, s32 dstHeight)
{
	s32 y;
	u8	**pp = pRowPointers;
	s32 factor = (srcHeight << PRECISION_SHIFT) / dstHeight; // Q8 fixed point
	for (y = 0; y < dstHeight; y++) {
		s32 tmp1 = factor * y;
		s32 tmp = tmp1 >> PRECISION_SHIFT; //floor(tmp1)

		pp[0] = (u8 *)pSrc + tmp * srcPitch;
		pp[1] = pp[0] + srcPitch;
		if ((tmp + 1) >= srcHeight) {
			pp[1] = pp[0];
		} else {
			pp[1] = pp[0] + srcPitch;
		}

		s32 v_ratio = tmp1 - (tmp << PRECISION_SHIFT);

		pCordinates[y] = v_ratio;
//		fprintf(stderr, "tmp:%d v_ratio:%d pp:%p srcPitch:%d\n", tmp, v_ratio, pp[0], srcPitch);
		pp += 2;
	}
}

// 1000 1002
// 
// 0010 0210
//
// 1002 1000

int bilinearBGR888(u8 *pDst, u32 dstPitch, s32 dstWidth, s32 dstHeight, const u8 *pSrc, u32 srcPitch, s32 srcWidth, s32 srcHeight)
{
	s32 y;

	s32 wVectors[dstWidth * 2];
	u8 *hSamples[dstHeight * 2];
	s16 U[dstWidth];
	s16 V[dstHeight];

	getWVectorsAndCordinates(wVectors, U, srcWidth, dstWidth);
	getHRowPointersAndCordinates(hSamples, V, pSrc, srcPitch, srcHeight, dstHeight);
	for (y = 0; y < dstHeight; y++) {
		//doBilinearLine(pDst, dstWidth, wVectors, &hSamples[y * 2], U, V[y]); 
		doBilinearLine_BGR888(pDst, dstWidth, wVectors, &hSamples[y * 2], U, V[y]); 
		pDst += dstPitch;
	}
	return 0;
}

#define TEST

#ifdef TEST
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "BMPFile.h"

#define ZOOMING	1.3f

int main()
{
	bool result;
	BMPFile bmpFile;
	result = bmpFile.Init("../../test/test.bmp");
	if (!result) {
		fprintf(stderr, "Unable to open the file\n");
		exit(1);
	}

	BMPFile::ImageInfo imageInfo;
	result = bmpFile.GetImageInfo(imageInfo);
	if (!result) {
		fprintf(stderr, "Unable to get image info\n");
		exit(1);
	}

	u8 *src, *dst;

	src = (u8 *)malloc(imageInfo.pitch * imageInfo.h);
	for (int i = 0; i < imageInfo.h; i++) {
		bmpFile.ReadScanline(src + i * imageInfo.pitch, imageInfo.pitch, 1);
	}
#if 1
	s32 dstWidth = (int)((float)imageInfo.w * ZOOMING);
	s32 dstHeight = (int)((float)imageInfo.h * ZOOMING);
#else
	u32 dstWidth = 32;
	u32 dstHeight = 32;
#endif
	
	u32 dstPitch = ((dstWidth * imageInfo.bpp / 8 + 3) / 4 * 4);
	u32 dstSize = dstPitch * dstHeight;
	dst = (u8 *)malloc(dstSize);
	memset(dst, 0, dstSize);
	fprintf(stderr, "srcp_start:%p srcp_end:%p\n", src, src + imageInfo.pitch * imageInfo.h);
	fprintf(stderr, "bpp:%d dstSize:%d\n", imageInfo.bpp / 8, dstSize);
	fprintf(stderr, "dst_w:%d dst_h:%d\n", dstWidth, dstHeight);

	struct timeval begin, end;
	gettimeofday(&begin, NULL);
	for (int i = 0; i < 100; i++) {
		bilinearBGR888(dst, dstPitch, dstWidth, dstHeight,
					   src, imageInfo.pitch, imageInfo.w, imageInfo.h);
	}
	gettimeofday(&end, NULL);
	fprintf(stderr, "%dmsec for %d times\n", (end.tv_sec - begin.tv_sec) * 1000 + (end.tv_usec - begin.tv_usec) / 1000, 100);

	int fd = open("test.bgr", O_RDWR|O_CREAT,0644);
	write(fd, dst, dstSize); 
	close(fd);
	return 0;
}
#endif
